<template>
  <a-layout class="_fc-designer" :style="'height:' + dragHeight">
    <a-layout-content>
      <!--组件-->
      <a-layout style="height: 100%">
        <a-layout-sider class="_fc-l" :width="255">
          <div
            class="_fc-l-group"
            v-for="(item, index) in menuList"
            :key="index"
          >
            <h4 class="_fc-l-title">{{ item.title }}</h4>
            <draggable
              :group="{ name: 'default', pull: 'clone', put: false }"
              :sort="false"
              itemKey="name"
              :list="item.list"
            >
              <template #item="{ element }">
                <div class="_fc-l-item">
                  <div class="_fc-l-icon">
                    <i class="fc-icon" :class="element.icon || 'icon-input'" />
                  </div>
                  <span class="_fc-l-name">
                    {{
                      t("components." + element.name + ".name") || element.label
                    }}
                  </span>
                </div>
              </template>
            </draggable>
          </div>
        </a-layout-sider>

        <!--拖拽-->
        <a-layout class="_fc-m">
          <a-layout-header class="_fc-m-tools" height="45">
            <slot name="handle" />
            <a-space>
              <a-button type="primary" size="small" @click="previewFc">
                {{ t("designer.preview") }}
              </a-button>
              <a-popconfirm
                content="清空后将不能恢复, 确定要清空吗"
                ok-text="清空"
                cancel-text="取消"
                @ok="clearDragRule"
              >
                <a-button type="primary" size="small">
                  {{ t("designer.clear") }}
                </a-button>
              </a-popconfirm>
            </a-space>
          </a-layout-header>
          <a-layout-content
            style="background: #f5f5f5 !important; padding: 10px"
          >
            <div class="_fc-m-drag">
              <DragForm
                :rule="dragForm.rule"
                :option="form.value"
                v-model:api="dragForm.api"
              />
            </div>
          </a-layout-content>
        </a-layout>

        <!--表单-->
        <a-layout-sider
          class="_fc-r"
          :width="400"
          v-if="!config || config.showConfig !== false"
        >
          <a-layout style="height: 100%">
            <a-layout-header height="40px" class="_fc-r-tabs">
              <div
                class="_fc-r-tab"
                :class="{ active: activeTab === 'props' }"
                v-if="
                  !!activeRule || (config && config.showFormConfig === false)
                "
                @click="activeTab = 'props'"
              >
                {{ t("designer.config.component") }}
              </div>
              <div
                class="_fc-r-tab"
                v-if="!config || config.showFormConfig !== false"
                :class="{ active: activeTab === 'form' && !!activeRule }"
                @click="activeTab = 'form'"
              >
                {{ t("designer.config.form") }}
              </div>
            </a-layout-header>
            <a-layout-content
              v-show="activeTab === 'form'"
              v-if="!config || config.showFormConfig !== false"
            >
              <a-card :bordered="false">
                <DragForm
                  :rule="form.rule"
                  :option="form.option"
                  :modelValue="form.value.form"
                  v-model="form.value.form"
                  v-model:api="form.api"
                />
              </a-card>
            </a-layout-content>
            <a-layout-content
              v-show="activeTab === 'props'"
              style="padding: 0 20px"
              :key="activeRule ? activeRule._id : ''"
            >
              <div>
                <a-divider v-if="showBaseRule">
                  {{ t("designer.config.rule") }}
                </a-divider>
                <DragForm
                  v-show="showBaseRule"
                  v-model:api="baseForm.api"
                  :rule="baseForm.rule"
                  :option="baseForm.options"
                  :modelValue="baseForm.value"
                  @change="baseChange"
                />
                <a-divider>{{ t("designer.config.props") }}</a-divider>
                <DragForm
                  v-model:api="propsForm.api"
                  :rule="propsForm.rule"
                  :option="propsForm.options"
                  :modelValue="propsForm.value"
                  @change="propChange"
                  @removeField="propRemoveField"
                />
                <a-divider v-if="showValidateRule">
                  {{ t("designer.config.validate") }}
                </a-divider>
                <DragForm
                  v-if="showBaseRule"
                  v-model:api="validateForm.api"
                  :rule="validateForm.rule"
                  :option="validateForm.options"
                  :modelValue="validateForm.value"
                  @update:modelValue="validateChange"
                />
              </div>
            </a-layout-content>
          </a-layout>
        </a-layout-sider>
        <a-drawer
          :visible="preview.state"
          width="800px"
          title="预览"
          @cancel="preview.state = false"
          :footer="false"
        >
          <ViewForm
            :rule="preview.rule"
            :option="preview.option"
            v-if="preview.state"
          />
        </a-drawer>
      </a-layout>
    </a-layout-content>
  </a-layout>
</template>
<script>
import form from "../config/base/form";
import field from "../config/base/field";
import validate from "../config/base/validate";
import { deepCopy } from "@form-create/utils/lib/deepextend";
import is, { hasProperty } from "@form-create/utils/lib/type";
import { lower } from "@form-create/utils/lib/tocase";
import ruleList from "../config/rule";
import draggable from "vuedraggable/src/vuedraggable";
import createMenu from "../config/menu";
import { upper, useLocale } from "../utils/index";
import { designerForm } from "../utils/form";
import viewForm from "../utils/form";
import { t as globalT } from "../utils/locale";
import {
  computed,
  reactive,
  toRefs,
  toRef,
  ref,
  getCurrentInstance,
  provide,
  nextTick,
  watch,
  defineComponent,
  markRaw,
} from "vue";

export default defineComponent({
  name: "FcDesigner",
  components: {
    draggable,
    DragForm: designerForm.$form(),
    ViewForm: viewForm.$form(),
  },
  props: ["menu", "height", "config", "mask", "locale"],
  setup(props) {
    const { menu, height, mask, locale } = toRefs(props);
    const vm = getCurrentInstance();
    const fcx = reactive({ active: null });
    provide("fcx", fcx);
    provide("designer", vm);

    const config = toRef(props, "config", {});
    const baseRule = toRef(config.value, "baseRule", null);
    const componentRule = toRef(config.value, "componentRule", {});
    const validateRule = toRef(config.value, "validateRule", null);
    const formRule = toRef(config.value, "formRule", null);
    const dragHeight = computed(() => {
      const h = height.value;
      if (!h) return "100%";
      return is.Number(h) ? `${h}px` : h;
    });
    let _t = globalT;
    if (locale.value) {
      _t = useLocale(locale).t;
    }
    const t = (...args) => _t(...args);

    const tidyRuleConfig = (orgRule, configRule, ...args) => {
      if (configRule) {
        if (is.Function(configRule)) {
          return configRule(...args);
        }
        if (configRule.rule) {
          let rule = configRule.rule(...args);
          if (configRule.append) {
            rule = [...rule, ...orgRule(...args)];
          }
          return rule;
        }
      }
      return orgRule(...args);
    };

    const data = reactive({
      cacheProps: {},
      moveRule: null,
      addRule: null,
      added: null,
      activeTab: "form",
      activeRule: null,
      children: ref([]),
      menuList: menu.value || createMenu({ t }),
      showBaseRule: false,
      visible: {
        preview: false,
      },
      t,
      preview: {
        state: false,
        rule: [],
        option: {},
      },
      dragForm: ref({
        rule: [],
        api: {},
      }),
      inputForm: {
        state: false,
        rule: [],
        option: {},
        api: {},
        data: {},
        key: "",
      },
      formOptions: {},
      oldOptionsKeys: [],
      form: {
        rule: tidyRuleConfig(form, formRule.value, { t }),
        api: {},
        option: {
          form: {
            layout: "vertical",
            formCreateSubmitBtn: true,
            formCreateResetBtn: false,
          },
          submitBtn: false,
        },
        value: {
          form: {
            layout: "horizontal",
            labelAlign: "right",
            autoLabelWidth: true,
            formCreateSubmitBtn: true,
            formCreateResetBtn: false,
          },
          submitBtn: false,
        },
      },
      baseForm: {
        rule: tidyRuleConfig(field, baseRule.value, { t }),
        api: {},
        value: {},
        options: {
          form: {
            layout: "vertical",
          },
          submitBtn: false,
          mounted: (fapi) => {
            fapi.activeRule = data.activeRule;
            fapi.setValue(fapi.options.formData || {});
          },
        },
      },
      validateForm: {
        rule: tidyRuleConfig(validate, validateRule.value, { t }),
        api: {},
        value: [],
        options: {
          form: {
            labelPosition: "top",
            size: "small",
            layout: "vertical",
          },
          submitBtn: false,
          mounted: (fapi) => {
            fapi.activeRule = data.activeRule;
            fapi.setValue(fapi.options.formData || {});
          },
        },
      },
      propsForm: {
        rule: [],
        api: {},
        value: {},
        options: {
          form: {
            labelPosition: "top",
            size: "small",
            layout: "vertical",
          },
          submitBtn: false,
          mounted: (fapi) => {
            fapi.activeRule = data.activeRule;
            fapi.setValue(fapi.options.formData || {});
          },
        },
      },
    });

    const showValidateRule = computed(() => {
      return !!data.validateForm?.rule.length;
    });

    watch(
      () => data.preview.state,
      function (n) {
        if (!n) {
          nextTick(() => {
            data.preview.rule = data.preview.option = null;
          });
        }
      }
    );

    let unWatchActiveRule = null;

    watch(
      () => locale.value,
      (n) => {
        _t = n ? useLocale(locale).t : globalT;
        const formVal = data.form.api.formData && data.form.api.formData();
        const baseFormVal =
          data.baseForm.api.formData && data.baseForm.api.formData();
        const validateFormVal =
          data.validateForm.api.formData && data.validateForm.api.formData();
        data.validateForm.rule = tidyRuleConfig(validate, validateRule.value, {
          t,
        });
        data.baseForm.rule = tidyRuleConfig(field, baseRule.value, { t });
        data.form.rule = tidyRuleConfig(form, formRule.value, { t });
        data.cacheProps = {};
        const rule = data.activeRule;
        let propsVal = null;
        if (rule) {
          propsVal =
            data.propsForm.api.formData && data.propsForm.api.formData();
          data.propsForm.rule = data.cacheProps[rule._id] = tidyRuleConfig(
            rule.config.config.props,
            componentRule.value && componentRule.value[rule.config.config.name],
            rule,
            {
              t,
              api: data.dragForm.api,
            }
          );
        }
        nextTick(() => {
          formVal && data.form.api.setValue(formVal);
          baseFormVal && data.baseForm.api.setValue(baseFormVal);
          validateFormVal && data.validateForm.api.setValue(validateFormVal);
          propsVal && data.propsForm.api.setValue(propsVal);
        });
      }
    );

    const methods = {
      unWatchActiveRule() {
        unWatchActiveRule && unWatchActiveRule();
        unWatchActiveRule = null;
      },
      watchActiveRule() {
        methods.unWatchActiveRule();
        unWatchActiveRule = watch(
          () => data.activeRule,
          function (n) {
            n && methods.updateRuleFormData();
          },
          { deep: true, flush: "post" }
        );
      },
      makeChildren(children) {
        return reactive({ children }).children;
      },
      getParent(rule) {
        let parent = rule.__fc__.parent.rule;
        const config = parent.config;
        if (config && config.config.inside) {
          rule = parent;
          parent = parent.__fc__.parent.rule;
        }
        return { root: parent, parent: rule };
      },
      makeDrag(group, tag, children, on) {
        return {
          type: "DragBox",
          wrap: {
            show: false,
          },
          col: {
            show: false,
          },
          inject: true,
          props: {
            rule: {
              props: {
                tag: "a-col",
                group: group === true ? "default" : group,
                ghostClass: "ghost",
                animation: 150,
                handle: "._fc-drag-btn",
                emptyInsertThreshold: 0,
                direction: "vertical",
                itemKey: "type",
              },
            },
            tag,
          },
          children,
          on,
        };
      },
      clearDragRule() {
        methods.setRule([]);
      },
      makeDragRule(children) {
        return methods.makeChildren([
          methods.makeDrag(true, "draggable", children, {
            add: (inject, evt) => methods.dragAdd(children, evt),
            end: (inject, evt) => methods.dragEnd(children, evt),
            start: (inject, evt) => methods.dragStart(children, evt),
            unChoose: (inject, evt) => methods.dragUnChoose(children, evt),
          }),
        ]);
      },
      previewFc() {
        data.preview.state = true;
        data.preview.rule = methods.getRule();
        data.preview.option = methods.getOption();
      },
      getRule() {
        return methods.parseRule(deepCopy(data.dragForm.api.rule[0].children));
      },
      getJson() {
        return designerForm.toJson(methods.getRule());
      },
      getOption() {
        const option = deepCopy(data.form.value);
        Object.keys(option.form._event || {}).forEach((k) => {
          if (option.form._event[k]) {
            option[k] = option.form._event[k];
          }
        });
        option.submitBtn = option._submitBtn;
        option.resetBtn = option._resetBtn;
        if (typeof option.submitBtn === "object") {
          option.submitBtn.show = data.form.value.form.formCreateSubmitBtn;
        } else {
          option.submitBtn = {
            show: data.form.value.form.formCreateSubmitBtn,
            innerText: t("form.submit"),
          };
        }
        if (typeof option.resetBtn === "object") {
          option.resetBtn.show = data.form.value.form.formCreateResetBtn;
        } else {
          option.resetBtn = {
            show: data.form.value.form.formCreateResetBtn,
            innerText: t("form.reset"),
          };
        }
        delete option.form._event;
        delete option.form.formCreateSubmitBtn;
        delete option.form.formCreateResetBtn;
        delete option._submitBtn;
        delete option._resetBtn;
        return option;
      },
      setOption(opt) {
        console.log(opt);
        let option = is.String(opt) ? JSON.parse(opt) : deepCopy(opt || {});
        option.form = {
          ...(option.form || {}),
        };
        option.form._event = {
          onSubmit: option.onSubmit || "",
          onCreated: option.onCreated || "",
          onMounted: option.onMounted || "",
          onChange: option.onChange || "",
          beforeFetch: option.beforeFetch || "",
        };
        option.form.formCreateSubmitBtn =
          typeof option.submitBtn === "object"
            ? option.submitBtn.show === undefined
              ? true
              : !!option.submitBtn.show
            : !!option.submitBtn;
        option.form.formCreateResetBtn =
          typeof option.resetBtn === "object"
            ? !!option.resetBtn.show
            : !!option.resetBtn;
        option._resetBtn = option.resetBtn;
        option.resetBtn = false;
        option._submitBtn = option.submitBtn;
        option.submitBtn = false;
        data.oldOptionsKeys = Object.keys(data.form.value);
        delete option.formData;
        data.formOptions = option;
        data.form.value = option;
      },
      setOptions(opt) {
        methods.setOption(opt);
      },
      getOptionsJson() {
        return designerForm.toJson([this.getOption()]).slice(1).slice(0, -1);
      },
      setRule(rules) {
        if (!rules) {
          rules = [];
        }
        data.children = ref(
          methods.loadRule(
            is.String(rules) ? designerForm.parseJson(rules) : deepCopy(rules)
          )
        );
        methods.clearActiveRule();
        data.dragForm.rule = methods.makeDragRule(
          methods.makeChildren(data.children)
        );
      },
      clearActiveRule() {
        data.activeRule = null;
        data.activeTab = "form";
      },
      loadRule(rules) {
        const loadRule = [];
        rules.forEach((rule) => {
          if (is.String(rule)) {
            return loadRule.push(rule);
          }
          const config = ruleList[rule._fc_drag_tag] || ruleList[rule.type];
          config && config.loadRule && config.loadRule(rule);
          const _children = rule.children;
          rule.children = [];
          if (rule.control) {
            rule._control = rule.control;
            delete rule.control;
          }
          if (config) {
            rule = methods.makeRule(config, rule);
            if (_children) {
              let children = rule.children[0].children;
              if (config.drag) {
                children = children[0].children;
              }
              children.push(...methods.loadRule(_children));
            }
          } else if (_children) {
            rule.children = methods.loadRule(_children);
          }
          loadRule.push(rule);
        });
        return loadRule;
      },
      parseRule(children) {
        return [...children].reduce((initial, rule) => {
          if (is.String(rule)) {
            initial.push(rule);
            return initial;
          } else if (rule.type === "DragBox") {
            initial.push(...methods.parseRule(rule.children));
            return initial;
          } else if (rule.type === "DragTool") {
            rule = rule.children[0];
            if (rule.type === "DragBox") {
              initial.push(...methods.parseRule(rule.children));
              return initial;
            }
          }
          if (!rule) {
            return initial;
          }
          rule = { ...rule };
          if (rule.children.length) {
            rule.children = methods.parseRule(rule.children);
          }
          delete rule._id;
          delete rule.key;
          delete rule.component;
          if (rule.config) {
            rule.config.config &&
              rule.config.config.parseRule &&
              rule.config.config.parseRule(rule);
            delete rule.config.config;
          }
          if (rule.effect) {
            delete rule.effect._fc;
            delete rule.effect._fc_tool;
          }
          if (rule._control) {
            rule.control = rule._control;
            delete rule._control;
          }
          Object.keys(rule)
            .filter(
              (k) =>
                (Array.isArray(rule[k]) && rule[k].length === 0) ||
                (is.Object(rule[k]) && Object.keys(rule[k]).length === 0)
            )
            .forEach((k) => {
              delete rule[k];
            });
          initial.push(rule);
          return initial;
        }, []);
      },
      baseChange(field, value, _, fapi) {
        if (data.activeRule && fapi[data.activeRule._id] === data.activeRule) {
          methods.unWatchActiveRule();
          data.activeRule[field] = value;
          methods.watchActiveRule();
          data.activeRule.config.config?.watch?.["$" + field]?.({
            field,
            value,
            api: fapi,
            rule: data.activeRule,
          });
        }
      },
      propRemoveField(field, _, fapi) {
        if (data.activeRule && fapi[data.activeRule._id] === data.activeRule) {
          methods.unWatchActiveRule();
          const org = field;
          data.dragForm.api.sync(data.activeRule);
          if (field.indexOf("formCreate") === 0) {
            field = field.replace("formCreate", "");
            if (!field) {
              return;
            }
            field = lower(field);
            if (field.indexOf("effect") === 0 && field.indexOf(">") > -1) {
              delete data.activeRule.effect[field.split(">")[1]];
            } else if (
              field.indexOf("props") === 0 &&
              field.indexOf(">") > -1
            ) {
              delete data.activeRule.props[field.split(">")[1]];
            } else if (
              field.indexOf("attrs") === 0 &&
              field.indexOf(">") > -1
            ) {
              delete data.activeRule.attrs[field.split(">")[1]];
            } else if (field === "child") {
              delete data.activeRule.children[0];
            } else if (field === "formCreateChild") {
              delete data.activeRule.children[0];
            } else if (field) {
              data.activeRule[field] = undefined;
            }
          } else {
            delete data.activeRule.props[field];
          }
          methods.watchActiveRule();
          data.activeRule.config.config?.watch?.[org]?.({
            field: org,
            value: undefined,
            api: fapi,
            rule: data.activeRule,
          });
        }
      },
      propChange(field, value, _, fapi) {
        if (data.activeRule && fapi[data.activeRule._id] === data.activeRule) {
          methods.unWatchActiveRule();
          const org = field;
          if (field.indexOf("formCreate") === 0) {
            field = field.replace("formCreate", "");
            if (!field) return;
            field = lower(field);
            if (field.indexOf("effect") === 0 && field.indexOf(">") > -1) {
              data.activeRule.effect[field.split(">")[1]] = value;
            } else if (
              field.indexOf("props") === 0 &&
              field.indexOf(">") > -1
            ) {
              data.activeRule.props[field.split(">")[1]] = value;
            } else if (
              field.indexOf("attrs") === 0 &&
              field.indexOf(">") > -1
            ) {
              data.activeRule.attrs[field.split(">")[1]] = value;
            } else if (field === "child") {
              data.activeRule.children[0] = value;
            } else {
              data.activeRule[field] = value;
            }
          } else if (field === "formCreateChild") {
            data.activeRule.children[0] = value;
          } else {
            data.activeRule.props[field] = value;
          }
          methods.watchActiveRule();
          data.activeRule.config.config?.watch?.[org]?.({
            field: org,
            value,
            api: fapi,
            rule: data.activeRule,
          });
        }
      },
      validateChange(formData) {
        if (
          !data.activeRule ||
          data.validateForm.api[data.activeRule._id] !== data.activeRule
        ) {
          return;
        }
        data.activeRule.validate = formData.validate || [];
        data.dragForm.api.refreshValidate();
        data.dragForm.api.nextTick(() => {
          data.dragForm.api.clearValidateState(data.activeRule.__fc__.id);
        });
      },
      toolActive(rule) {
        methods.unWatchActiveRule();
        if (data.activeRule) {
          delete data.propsForm.api[data.activeRule._id];
          delete data.baseForm.api[data.activeRule._id];
          delete data.validateForm.api[data.activeRule._id];
          delete data.dragForm.api.activeRule;
        }
        data.activeRule = rule;
        data.dragForm.api.activeRule = rule;

        nextTick(() => {
          data.activeTab = "props";
          nextTick(() => {
            data.propsForm.api[data.activeRule._id] = data.activeRule;
            data.baseForm.api[data.activeRule._id] = data.activeRule;
            data.validateForm.api[data.activeRule._id] = data.activeRule;
          });
        });
        if (!data.cacheProps[rule._id]) {
          data.cacheProps[rule._id] = tidyRuleConfig(
            rule.config.config.props,
            componentRule.value && componentRule.value[rule.config.config.name],
            rule,
            {
              t,
              api: data.dragForm.api,
            }
          );
        }
        data.propsForm.rule = data.cacheProps[rule._id];
        methods.updateRuleFormData();
        methods.watchActiveRule();
      },
      updateRuleFormData() {
        const rule = data.activeRule;
        const formData = {
          ...rule.props,
          formCreateChild: deepCopy(rule.children[0]),
        };
        Object.keys(rule).forEach((k) => {
          if (["effect", "config", "payload", "id", "type"].indexOf(k) < 0)
            formData["formCreate" + upper(k)] = deepCopy(rule[k]);
        });
        ["props", "effect", "attrs"].forEach((name) => {
          rule[name] &&
            Object.keys(rule[name]).forEach((k) => {
              formData["formCreate" + upper(name) + ">" + k] = deepCopy(
                rule[name][k]
              );
            });
        });
        data.propsForm.value = formData;
        data.showBaseRule =
          hasProperty(rule, "field") &&
          rule.input !== false &&
          (!config.value || config.value.showBaseForm !== false);
        if (data.showBaseRule) {
          data.baseForm.value = {
            field: rule.field,
            title: rule.title || "",
            info: rule.info,
            _control: rule._control,
          };
          data.validateForm.value = {
            validate: rule.validate ? [...rule.validate] : [],
          };
          data.dragForm.api.refreshValidate();
          data.dragForm.api.nextTick(() => {
            data.dragForm.api.clearValidateState(rule.__fc__.id);
          });
        }
      },
      dragStart(children) {
        data.moveRule = children;
        data.added = false;
      },
      dragUnChoose(children, evt) {
        data.addRule = {
          children,
          oldIndex: evt.oldIndex,
        };
      },
      dragAdd(children, evt) {
        const newIndex = evt.newIndex;
        const menu = evt.item._underlying_vm_;
        if (!menu || menu.__fc__) {
          if (data.addRule) {
            const rule = data.addRule.children.splice(data.addRule.oldIndex, 1);
            children.splice(newIndex, 0, rule[0]);
          }
        } else {
          const rule = methods.makeRule(ruleList[menu.name]);
          children.splice(newIndex, 0, rule);
        }
        data.added = true;
        // data.dragForm.api.refresh();
      },
      dragEnd(children, { newIndex, oldIndex }) {
        if (
          !data.added &&
          !(data.moveRule === children && newIndex === oldIndex)
        ) {
          const rule = data.moveRule.splice(oldIndex, 1);
          children.splice(newIndex, 0, rule[0]);
        }
        data.moveRule = null;
        data.addRule = null;
        data.added = false;
        // data.dragForm.api.refresh();
      },
      makeRule(config, _rule) {
        const rule = _rule || config.rule({ t });
        rule.config = { config };
        if (config.component) {
          rule.component = markRaw(config.component);
        }
        if (!rule.effect) {
          rule.effect = {};
        }
        rule.effect._fc = true;
        rule._fc_drag_tag = config.name;

        let drag;

        if (config.drag) {
          rule.children.push(
            (drag = methods.makeDrag(
              config.drag,
              rule.type,
              methods.makeChildren([]),
              {
                end: (inject, evt) =>
                  methods.dragEnd(inject.self.children, evt),
                add: (inject, evt) =>
                  methods.dragAdd(inject.self.children, evt),
                start: (inject, evt) =>
                  methods.dragStart(inject.self.children, evt),
                unChoose: (inject, evt) =>
                  methods.dragUnChoose(inject.self.children, evt),
              }
            ))
          );
        }
        if (config.children && !_rule) {
          for (let i = 0; i < (config.childrenLen || 1); i++) {
            const child = methods.makeRule(ruleList[config.children]);
            (drag || rule).children.push(child);
          }
        }
        const dragMask =
          mask.value !== undefined
            ? mask.value !== false
            : config.mask !== false;
        if (config.inside) {
          rule.children = methods.makeChildren([
            {
              type: "DragTool",
              props: {
                dragBtn: config.dragBtn !== false,
                children: config.children,
                mask: dragMask,
              },
              effect: {
                _fc_tool: true,
              },
              inject: true,
              on: {
                delete: ({ self }) => {
                  const parent = methods.getParent(self).parent;
                  parent.__fc__.rm();
                  vm.emit("delete", parent);
                  methods.clearActiveRule();
                },
                create: ({ self }) => {
                  const top = methods.getParent(self);
                  vm.emit("create", top.parent);
                  top.root.children.splice(
                    top.root.children.indexOf(top.parent) + 1,
                    0,
                    methods.makeRule(top.parent.config.config)
                  );
                },
                addChild: ({ self }) => {
                  const top = methods.getParent(self);
                  const config = top.parent.config.config;
                  const item = ruleList[config.children];
                  if (!item) {
                    return;
                  }
                  (!config.drag
                    ? top.parent
                    : top.parent.children[0]
                  ).children[0].children.push(methods.makeRule(item));
                },
                copy: ({ self }) => {
                  const top = methods.getParent(self);
                  vm.emit("copy", top.parent);
                  top.root.children.splice(
                    top.root.children.indexOf(top.parent) + 1,
                    0,
                    designerForm.copyRule(top.parent)
                  );
                },
                active: ({ self }) => {
                  const top = methods.getParent(self);
                  vm.emit("active", top.parent);
                  methods.toolActive(top.parent);
                },
              },
              children: rule.children,
            },
          ]);
          return rule;
        } else {
          return {
            type: "DragTool",
            props: {
              dragBtn: config.dragBtn !== false,
              children: config.children,
              mask: dragMask,
            },
            effect: {
              _fc_tool: true,
            },
            inject: true,
            on: {
              delete: ({ self }) => {
                vm.emit("delete", self.children[0]);
                self.__fc__.rm();
                methods.clearActiveRule();
              },
              create: ({ self }) => {
                vm.emit("create", self.children[0]);
                const top = methods.getParent(self);
                top.root.children.splice(
                  top.root.children.indexOf(top.parent) + 1,
                  0,
                  methods.makeRule(self.children[0].config.config)
                );
              },
              addChild: ({ self }) => {
                const config = self.children[0].config.config;
                const item = ruleList[config.children];
                if (!item) {
                  return;
                }
                (!config.drag
                  ? self
                  : self.children[0]
                ).children[0].children.push(methods.makeRule(item));
              },
              copy: ({ self }) => {
                vm.emit("copy", self.children[0]);
                const top = methods.getParent(self);
                top.root.children.splice(
                  top.root.children.indexOf(top.parent) + 1,
                  0,
                  designerForm.copyRule(top.parent)
                );
              },
              active: ({ self }) => {
                vm.emit("active", self.children[0]);
                methods.toolActive(self.children[0]);
              },
            },
            children: methods.makeChildren([rule]),
          };
        }
      },
    };
    data.dragForm.rule = methods.makeDragRule(
      methods.makeChildren(data.children)
    );
    return {
      ...toRefs(data),
      ...methods,
      dragHeight,
      t,
      showValidateRule,
    };
  },
  created() {
    document.body.ondrop = (e) => {
      e.preventDefault();
      e.stopPropagation();
    };
  },
});
</script>
<style>
._fc-designer {
  height: 100%;
  min-height: 500px;
  overflow: hidden;
  cursor: default;
  position: relative;
  background-color: #fff;
  --fc-drag-empty: "拖拽左侧列表中的组件到此处";
  --fc-child-empty: "点击右下角 \e789  按钮添加一列";
  --fc-tool-border-color: #2e73ff;
}

._fc-designer > .arco-layout-content {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 0;
}

._fc-designer > .arco-form-item-content-flex {
  background-color: #f5f5f5;
}

._fc-m .form-create ._fc-l-item {
  background: rgb(var(--primary-6));
  width: 100%;
  height: 10px;
  overflow: hidden;
  transition: all 0.3s ease;
}

._fc-l,
._fc-m,
._fc-r {
  border-top: 1px solid #ececec;
  box-sizing: border-box;
}

._fc-l-group {
  padding: 0 12px;
}

._fc-l-title {
  font-weight: 600;
  font-size: 14px;
  margin: 18px 0px 5px;
}

._fc-l-item {
  display: inline-block;
  background: #fff;
  color: #000;
  min-width: 70px;
  width: 33.33%;
  height: 70px;
  line-height: 1;
  text-align: center;
  transition: all 0.2s ease;
  cursor: pointer;
}

._fc-l-item i {
  font-size: 21px;
  display: inline-block;
}

._fc-l-item ._fc-l-name {
  font-size: 12px;
}

._fc-l-item ._fc-l-icon {
  padding: 10px 5px 12px;
}

._fc-l-item:hover {
  background: rgb(var(--primary-6));
  color: #fff;
}

._fc-m-tools {
  height: 40px;
  align-items: center;
  display: flex;
  justify-content: flex-end;
  border: 1px solid #ececec;
  border-top: 0 none;
}

._fc-m-tools button.arco-btn {
  padding: 5px 14px;
  display: flex;
  align-items: center;
}

._fc-m-tools .fc-icon {
  font-size: 14px;
  margin-right: 2px;
}

._fc-r .a-tabs__nav-wrap::after {
  height: 1px;
  background-color: #ececec;
}

._fc-r ._fc-r-tabs {
  display: flex;
  padding: 0;
  border-bottom: 1px solid #ececec;
}

._fc-r ._fc-r-tab {
  height: 40px;
  box-sizing: border-box;
  line-height: 40px;
  display: inline-block;
  list-style: none;
  font-size: 14px;
  font-weight: 600;
  color: #303133;
  position: relative;
  flex: 1;
  text-align: center;
}

._fc-r ._fc-r-tab.active {
  color: rgb(var(--primary-6));
  border-bottom: 2px solid rgb(var(--primary-6));
}

.drag-box {
  min-height: 60px;
  height: 100%;
  width: 100%;
}

._fc-m-drag {
  overflow: auto;
  padding: 2px;
  box-sizing: border-box;
}

._fc-m-drag,
.draggable-drag {
  background: #fff;
  height: 100%;
  position: relative;
}

._fc-m-drag > form,
._fc-m-drag > form > .arco-row {
  height: 100%;
}
</style>
